package net.sf.calibur.engine;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.calibur.engine.task.Handler;
import net.sf.calibur.engine.task.Task;

public class CaliburEngine {
	private String name;
	private Map<String, ExecutorPool> pools = new ConcurrentHashMap<String, ExecutorPool>();
	private Logger logger = LoggerFactory.getLogger(getClass());
	private Map<Integer, Handler> registCommands = new ConcurrentHashMap<Integer, Handler>();

	public CaliburEngine(String name) {
		this.name = name;
	}

	public String getPoolName(String name) {
		return new StringBuffer().append(getName()).append('-').append(name)
				.toString();
	}

	public String getName() {
		return name;
	}

	private void checkDuplicatedCommand(Handler[] groups) {
		Object key;
		Handler existed;
		for (Handler h : groups) {
			for (Task t : h.getAllTasks().values()) {
				key = t.getEventId();
				if (key.getClass() == int.class
						|| key.getClass() == Integer.class) {
					existed = registCommands.get(key);
					if (existed != null) {
						throw new IllegalArgumentException(
								String.format(
										"Cannot add duplicated command ID %d in handler %s and %s,",
										key, existed.getName(), h.getName()));
					} else {
						registCommands.put(key.hashCode(), h);
					}
				}
			}
		}
	}

	public ExecutorPool addExecutorPool(String name, int size,
			EventRouter router, Handler... groups) {
		checkDuplicatedCommand(groups);
		
		String poolName = getPoolName(name);
		ExecutorPool pool = pools.get(poolName);
		if (pool != null) {
			throw new IllegalArgumentException("Executor pool name " + name
					+ " already exists in executor engine.");
		}

		pool = new ExecutorPool(this, poolName, size, router, groups);
		pools.put(poolName, pool);
		return pool;
	}

	public void addEvent(Event event) {
		logger.info("engine accepted event " + event);
		for (ExecutorPool pool : pools.values()) {
			pool.onEvent(event);
		}
	}

	public void start() {
		for (ExecutorPool pool : pools.values()) {
			pool.start();
		}
	}

	public void shutdown() {
		for (ExecutorPool pool : pools.values()) {
			pool.shutdown();
		}
	}
}
